import { Type} from './type';
import { getInjectionToken} from './Inject';
import {
    Token, InjectionToken,Provider, ValueProvider, ClassProvider, FactoryProvider,
    isClassProvider, isValueProvider, isFactoryProvider} from './provider';
const DESIGN_PARAMTYPES = "design:paramtypes";    
export class Container{
    public providers = new Map<Token<any>,Provider<any>>();
    //注册提供者
    addProvider<T>(provider:Provider<T>){
        //provide就是token或者说标识符 
        this.providers.set(provider.provide,provider);
    }
    //获取对应的服务
    inject(type:Token<any>){
        let provider = this.providers.get(type);
        return this.injectWithProvider(type,provider);
    }
    injectWithProvider<T>(type: Token<T>, provider:Provider<T>){
        if(provider === undefined){//说明这个token没有对应任何的provider
            throw new Error(`No provider for type ${this.getTokenName(type)}`);
        }
        if (isClassProvider(provider)){
            return this.injectClass(provider);
        }else if(isValueProvider(provider)){
            return this.injectValue(provider);
        }else if(isFactoryProvider(provider)){
            return this.injectFactory(provider);
        }
    }
    injectClass<T>(provider: ClassProvider<T>): T {
        const target = provider.useClass;
        let params = this.getInjectedParams(target);
        return Reflect.construct(target, params);
        //return new provider.useClass();
    }
    //从类上获取到注入的参数
    getInjectedParams<T>(target:Type<T>){
        const argTypes = <Array<Type<any>>|undefined>Reflect.getMetadata(DESIGN_PARAMTYPES,target);
        if (argTypes === undefined)
            return [];
        //把数组做一个转换,从type数组转成此type对应的提供者的实例   
        return argTypes.map((argType,index)=>{
            const overrideToken = getInjectionToken(target,index);
            const actualToken = overrideToken === undefined ? argType : overrideToken;
            let provider = this.providers.get(actualToken);
            return this.injectWithProvider(actualToken,provider);
        });
    }

    getTokenName<T>(type: Token<T>){//type是一个类
        return type instanceof InjectionToken ? type.injectionIdentifier:type.name;
    }
    injectValue<T>(provider:ValueProvider<T>):T{
        return provider.useValue;
    }
    injectFactory<T>(provider: FactoryProvider<T>): T {
        return provider.useFactory();
    }
}
/* class Person{}
Person.name; */